{{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// I2C Electrically Erasable Programmable Read Only Memory Engine
//
// Author: Kwabena W. Agyeman
// Updated: 7/27/2010
// Designed For: P8X32A
// Version: 1.1
//
// Copyright (c) 2010 Kwabena W. Agyeman
// See end of file for terms of use.
//
// Update History:
//
// v1.0 - Original release - 8/8/2009.
// v1.1 - Added variable pin support - 7/27/2010.
//
// For each included copy of this object only one spin interpreter should access it at a time.
//
// Nyamekye,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// I2C Circuit:
//
//                  3.3V
//                   |
//                   R 10KOHM
//                   |
// Data Pin Number  --- EEPROM SDA Pin.
//
//                  3.3V
//                   |
//                   R 10KOHM
//                   |
// Clock Pin Number --- EEPROM SCL Pin.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}}

PUB readByte(EEPROMaddress) '' 18 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Reads a byte from the EEPROM. It is recommended to read the byte on a byte boundary.
'' //
'' // Returns the byte on success and false on failure. Could return a byte of value 0.
'' //
'' // EEPROMaddress - Starting byte address of the data to access.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  result &= readPage(EEPROMaddress, @result, 1)

PUB writeByte(EEPROMaddress, value) '' 19 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Writes a byte to the EEPROM. It is recommended to write the byte on a byte boundary.
'' //
'' // Returns true on success and false on failure.
'' //
'' // EEPROMaddress - Starting byte address of the data to access.
'' // Value - Value to write to the EEPROM.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  return writePage(EEPROMaddress, @value, 1)

PUB readWord(EEPROMaddress) '' 18 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Reads a word from the EEPROM. It is recommended to read the word on a word boundary.
'' //
'' // Returns the word on success and false on failure. Could return a word of value 0.
'' //
'' // EEPROMaddress - Starting byte address of the data to access.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  result &= readPage(EEPROMaddress, @result, 2)

PUB writeWord(EEPROMaddress, value) '' 19 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Writes a word to the EEPROM. It is recommended to write the word on a word boundary.
'' //
'' // Returns true on success and false on failure.
'' //
'' // EEPROMaddress - Starting byte address of the data to access.
'' // Value - Value to write to the EEPROM.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  return writePage(EEPROMaddress, @value, 2)

PUB readLong(EEPROMaddress) '' 18 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Reads a long from the EEPROM. It is recommended to read the long on a long boundary.
'' //
'' // Returns the long on success and false on failure. Could return a long of value 0.
'' //
'' // EEPROMaddress - Starting byte address of the data to access.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  result &= readPage(EEPROMaddress, @result, 4)

PUB writeLong(EEPROMaddress, value) '' 19 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Writes a long to the EEPROM. It is recommended to write the long on a long boundary.
'' //
'' // Returns true on success and false on failure.
'' //
'' // EEPROMaddress - Starting byte address of the data to access.
'' // Value - Value to write to the EEPROM.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  return writePage(EEPROMaddress, @value, 4)

PUB readPage(EEPROMaddress, RAMaddress, byteCount) '' 14 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Reads bytes from the EEPROM. Uses a 19 bit addressing scheme which can acess multiple EERPOMs on a bus.
'' //
'' // This rountine can only read from the EEPROM selected. Any reads past that EEPROM will warp arround.
'' //
'' // It is recommended to use the byte/word/long read rountines used on byte/word/long boundaries respectively.
'' //
'' // Returns true on success and false on failure.
'' //
'' // EEPROMaddress - Starting byte address of the data to access.
'' // RAMaddress - Starting byte address of the data to write to.
'' // ByteCount - Number of bytes to read.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  result := eepromPoll(EEPROMaddress)

  if(result)

    stopDataTransfer
    startDataTransfer

    result and= transmitPacket(constant((10 << 4) | 1) | ((EEPROMaddress >> 15) & $E))

    repeat ((byteCount <# (65_536 - (RAMaddress #> 0))) #> 0)
      byte[RAMaddress++] := receivePacket(--byteCount)

  stopDataTransfer
  clearLock

PUB writePage(EEPROMaddress, RAMaddress, byteCount) '' 14 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Writes bytes to the EEPROM. Uses a 19 bit addressing scheme which can access multiple EERPOMs on a bus.
'' //
'' // This rountine can only write to the page of the EEPROM selected. Any writes past that page will warp arround.
'' //
'' // It is recommended to use the byte/word/long write rountines used on byte/word/long boundaries respectively.
'' //
'' // Returns true on success and false on failure.
'' //
'' // EEPROMaddress - Starting byte address of the data to access.
'' // RAMaddress - Starting byte address of the data to read from.
'' // ByteCount - Number of bytes to write.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  result := eepromPoll(EEPROMaddress)

  if(result)

    repeat ((byteCount <# (65_536 - (RAMaddress #> 0))) #> 0)
      result and= transmitPacket(byte[RAMaddress++])

  stopDataTransfer
  clearLock

PUB ROMEngineStart(dataPinNumber, clockPinNumber, lockNumberToUse) '' 9 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Checks out a lock for the driver and changes the I2C Circuit pins.
'' //
'' // Returns true on success and false on failure.
'' //
'' // DataPinNumber - Pin to use to drive the SDA data line circuit.
'' // ClockPinNumber - Pin to use to drive the SCL clock line circuit.
'' // LockNumberToUse - Lock number to use if sharing the I2C bus (0 - 7). -1 to request a new lock number.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  ROMEngineStop
  dataPin := ((dataPinNumber <# 31) #> 0)
  clockPin := ((clockPinNumber <# 31) #> 0)
  if((dataPin <> clockPin) and (chipver == 1))
    lockNumber := lockNumberToUse
    if(lockNumberToUse == -1)
      lockNumber := locknew
    result or= ++lockNumber

PUB ROMEngineStop '' 3 Stack Longs

'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Returns the lock used by the driver.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  if(lockNumber)
    lockret(-1 + lockNumber~)

PRI eepromPoll(EEPROMaddress) ' 8 Stack Longs

  setLock
  startDataTransfer
  result := cnt

  repeat until(transmitPacket(constant(10 << 4) | ((EEPROMaddress >> 15) & $E)))

    stopDataTransfer
    startDataTransfer

    if((||(cnt - result)) > (clkfreq / constant(1_000 / 5)))
      return false

  result := transmitPacket(EEPROMaddress >> 8)
  result and= transmitPacket(EEPROMaddress)

PRI transmitPacket(value) ' 4 Stack Longs

  value := ((!value) >< 8)

  repeat 8
    dira[dataPin] := value
    dira[clockPin] := false
    dira[clockPin] := true
    value >>= 1

  dira[dataPin] := false
  dira[clockPin] := false
  result := not(ina[dataPin])
  dira[clockPin] := true
  dira[dataPin] := true

PRI receivePacket(aknowledge) ' 4 Stack Longs

  dira[dataPin] := false

  repeat 8
    result <<= 1
    dira[clockPin] := false
    result |= ina[dataPin]
    dira[clockPin] := true

  dira[dataPin] := (not(not(aknowledge)))
  dira[clockPin] := false
  dira[clockPin] := true
  dira[dataPin] := true

PRI startDataTransfer ' 3 Stack Longs

  outa[dataPin] := false
  outa[clockPin] := false
  dira[dataPin] := true
  dira[clockPin] := true

PRI stopDataTransfer ' 3 Stack Longs

  dira[clockPin] := false
  dira[dataPin] := false

PRI setLock ' 3 Stack Longs

  if(lockNumber)
    repeat while(lockset(lockNumber - 1))

PRI clearLock ' 3 Stack Longs

  if(lockNumber)
    lockclr(lockNumber - 1)

DAT

' //////////////////////Variable Array/////////////////////////////////////////////////////////////////////////////////////////

dataPin                 byte 29 ' Default data pin.
clockPin                byte 28 ' Default clock pin.
lockNumber              byte 00 ' Driver lock number.

' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

{{

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                  TERMS OF USE: MIT License
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}}